package org.summerframework.component.security.core.interceptor;

import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.summerframework.component.security.annotion.CIDRequired;
import org.summerframework.component.security.resolver.id.CIDService;
import org.summerframework.component.security.core.exception.CIDConvertException;
import org.summerframework.core.base.result.DefaultResultCode;
import org.summerframework.core.interceptor.AbstractHandlerInterceptorAdapter;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import org.summerframework.component.security.core.util.CIDUtil;

/**
 * 清理CID上下文
 * <p>
 * 当isCreate = false时, cid 必须有值, 并且 param中cid与id必须为空
 * <p>
 * 自动注入至method对应的对象中不影响原逻辑
 *
 * @author 石超
 * @version v1.0.0
 */
public class CIDInterceptor extends AbstractHandlerInterceptorAdapter {
    @Resource
    private ApplicationContext applicationContext;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    private final static ThreadLocal<CID> CID_CONTEXT = ThreadLocal.withInitial(CIDInterceptor.CID::new);

    public static class CID {
        private String cid;
        private Long id;

        public String getCid() {
            return cid;
        }

        public void setCid(String cid) {
            this.cid = cid;
        }

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        public void setId(String id) {
            if (id != null) {
                this.id = Long.valueOf(id);
            }
        }
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        CID cid = CID_CONTEXT.get();

        HandlerMethod handlerMethod = (HandlerMethod) handler;

        CIDRequired cidRequired = handlerMethod.getMethodAnnotation(CIDRequired.class);

        if (cidRequired == null) {
            return true;
        }

        @SuppressWarnings("unchecked")
        Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
        String tempCidValue = uriTemplateVars.get(cidRequired.cid());

        String cidVaule = ServletRequestUtils.getStringParameter(request, cidRequired.cid(), null);
        String idVaule = ServletRequestUtils.getStringParameter(request, cidRequired.id(), null);

        if (cidRequired.isCreate()) {
            cid.setCid(CIDUtil.createCIDName(redisTemplate.opsForHash(), cidRequired.cidSerivce().getName()));
        } else {
            if (cidRequired.isAuto() && idVaule != null) {
                cid.setCid(cidVaule);
                cid.setId(idVaule);
            } else {
                //当isCreate = false时, cid 必须有值, 并且 param中cid与id必须为空
                if ((cidVaule != null && tempCidValue != null) || idVaule != null) {
                    throw new CIDConvertException(DefaultResultCode.ERROR_URL_REQUEST, String.format("不能在path和param中同时拥有cid,并且param中不能传id, pathCid=%s, paramCid=%s, paramId=%s", tempCidValue, cidVaule, idVaule));
                }

                if (cidVaule == null) {
                    cidVaule = tempCidValue;
                }

                cid.setCid(cidVaule);
                CIDService cidService = applicationContext.getBean(cidRequired.cidSerivce());
                cid.setId(cidService.getId(cidVaule));
            }
        }

        uriTemplateVars.put(cidRequired.cid(), cid.getCid());

        if (cid.getId() != null) {
            uriTemplateVars.put(cidRequired.id(), cid.getId().toString());
        }

        return true;
    }

    public static Long getId(HandlerMethod handlerMethod, HttpServletRequest request) {
        CIDRequired cidRequired = handlerMethod.getMethodAnnotation(CIDRequired.class);

        if (cidRequired == null) {
            return null;
        }

        @SuppressWarnings("unchecked")
        Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);

        Object id = uriTemplateVars.get(cidRequired.id());

        return id != null ? Long.valueOf(id.toString()) : null;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        CID_CONTEXT.remove();
    }

    public static CID getCID() {
        return CID_CONTEXT.get();
    }
}
